#!/usr/bin/env python
# coding: utf-8

# Copyright (c) 2020 Huawei Technologies Co., Ltd.
# oec-hardware is licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Create: 2020-04-01

import time
import random
import hashlib
import os

from .command import Command
from .device import CertDevice

try:
    from urllib.parse import urlencode
    from urllib.request import urlopen, Request
except ImportError:
    from urllib import urlencode
    from urllib2 import urlopen, Request


class YieldIP(object):
    '''
    # @Method:用于生产不重复的IP地址
    # @Param:startip:初始ip地址
    # @Return：
    # @ author:cwx927323
    # @ ModifyRecord
    '''

    def __init__(self, mac):
        if not mac:
            self.mac = ':'.join([hex(random.choice(range(256)))[2:].rjust(2, '0') for i in range(6)])
        self.mac = str(mac)

    def _hash_str_to_int(self, strs, maxnum=False):
        maxnum = maxnum if maxnum else 255
        return int(hashlib.sha512(strs.encode('utf-8')).hexdigest(), 16) % int(maxnum)

    def str_to_netip(self, strs, maxnum=False):
        ipn = self._hash_str_to_int(strs, maxnum)
        n = 0
        while ipn <= 0:
            strs += str(random.random())
            ipn = self._hash_str_to_int(strs, maxnum)
            n += 1
            if n > 5:
                break
        return str(ipn)

    def compare_net(self, net1_ip, net2_ip):
        net1 = ".".join(net1_ip.split(".")[:3])
        net2 = ".".join(net2_ip.split(".")[:3])

        if net1 == net2:
            return False
        else:
            return True

    def generate_network(self, hostip):
        self.time = str(time.time())
        a = str(self.mac) + self.time
        ip0 = self.str_to_netip(str(self.mac) + self.time, maxnum=223)
        ip1 = self.str_to_netip(self.mac)
        ip2 = self.str_to_netip(self.time)
        ip = "%s.%s.%s.0" % (ip0, ip1, ip2)
        while True:
            if self.compare_net(ip, hostip) and int(ip0) <= 223 and int(ip0) != 127:
                return ip
            else:
                time.sleep(0.1)
                self.time = str(time.time())
                ip0 = self.str_to_netip(str(self.mac) + self.time, maxnum=223)
                ip1 = self.str_to_netip(self.mac)
                ip2 = self.str_to_netip(self.time)
                ip = "%s.%s.%s.0" % (ip0, ip1, ip2)

    def getip(num, startIp=None):
        ip_list = startIp.split(".")
        ip_list[3] = "1"
        n = 1
        while n < num:
            ip_list[3] = int(ip_list[3]) + 1
            if ip_list[3] >= 255:
                ip_list[3] = '1'
            cur_ip = ".".join('%s' % i for i in ip_list)
            n = n + 1


class CompassCI(object):
    def __init__(self, test_factory, card_info):
        self.auto_test_conf = card_info
        self.test_factory = test_factory
        self.client = None
        self.card_unique_identify = {}
        self.test_port = []
        self.id_paths = []

    def autoconfig(self):
        """
        read configuration from config file and set test_factory
        :return:
        """
        # 获取要测试的板卡
        if not self.get_card():
            print("要测试的板卡不存在！")
            return False
        else:
            print("要测试的板卡存在！")

        # 网卡配置客户端和服务端配置IP地址
        if not self.config_ip():
            print("IP地址配置失败！")
            return False
        else:
            print("IP地址配置完成！")

        # 设置要测试卡run的值为True
        self.set_case_status()

        print("enter autoconfig")
        return True

    def config_ip(self):
        """
        Configure client and server IP addresses
        """
        card_port = []
        if self.auto_test_conf["test_card_name"] == "ethernet":
            # 获取网卡所有端口
            for id_path in self.id_paths:
                for test_case in self.test_factory:
                    if id_path == test_case["device"].get_property("ID_PATH"):
                        card_port.append(test_case["device"].get_property("INTERFACE"))
            # 获取状态为connected的端口
            con_port = Command("ip link show up|grep 'state UP'").read().split('\n')
            for con_port in con_port:
                tem_port = con_port.split(':')[1].strip()
                if tem_port in card_port:
                    self.test_port.append(tem_port)
                    break

            if not self.test_port:
                print("All ports are down!")
                return False

            # 配置客户端IP地址
            server_ip = self.config_client_ip()

            # 配置服务端IP地址
            if not self.config_server_ip(server_ip):
                print("服务端IP配置失败")
                return False
        return True

    def get_card(self):
        """
        get test card
        :return:
        """
        # 判断板卡是否存在
        card_id = self.auto_test_conf["test_card_id"].split('-')
        self.card_unique_identify = {"vendorID": card_id[0], "deviceID": card_id[1],
                                     "svID": card_id[3], "ssID": card_id[2]}
        if self.auto_test_conf["test_card_name"] == "ethernet":
            for device in CertDevice.device_quadruples:
                if self.card_unique_identify == device.get_quadruples():
                    self.id_paths.append("pci-" + device.get_property("PCI_SLOT_NAME"))
            if not self.id_paths:
                return False

        return True

    def set_case_status(self):
        """
         set test case status
         :return:
         """
        for test_factory in self.test_factory:
            test_factory["run"] = False
        if self.auto_test_conf['test_card_name'] == 'ethernet':
            for test_factory in self.test_factory:
                for port in self.test_port:
                    if test_factory["device"].get_property("ID_NET_NAME_PATH") == port:
                        test_factory["run"] = True
        else:
            for test_factory in self.test_factory:
                if test_factory['name'] == self.auto_test_conf['test_card_name']:
                    test_factory['run'] = True
        return True

    def config_server_ip(self, ip):
        """
         config server ip
         :return:
        """
        form = {}
        server = os.getenv('server')
        vendorID = self.card_unique_identify["vendorID"][2:4] + self.card_unique_identify["vendorID"][0:2]
        deviceID = self.card_unique_identify["deviceID"][2:4] + self.card_unique_identify["deviceID"][0:2]
        ssID = self.card_unique_identify["ssID"][2:4] + self.card_unique_identify["ssID"][0:2]
        svID = self.card_unique_identify["svID"][2:4] + self.card_unique_identify["svID"][0:2]
        card_id = vendorID + deviceID + svID + ssID
        form['startip'] = ip
        form['cardid'] = card_id
        url = 'http://{}/api/config/ip'.format(server)
        data = urlencode(form).encode('utf8')
        headers = {
            'Content-type': 'application/x-www-form-urlencoded',
            'Accept': 'text/plain'
        }
        try:
            req = Request(url, data=data, headers=headers)
            res = urlopen(req)
            if res.code != 200:
                print("Error: upload failed, %s" % res.msg)
                return False
            return True
        except Exception as exp:
            print(exp)
            return False

    def config_client_ip(self):
        """
        config client ip
        :return:
        """
        local_ip = os.getenv("client")
        v4ip = YieldIP('')
        v4network = v4ip.generate_network(local_ip).split('.')
        num = 2
        for port in self.test_port:
            ip = '.'.join(v4network[0:3]) + '.' + str(int(v4network[3]) + num)
            Command("ifconfig %s %s\/24" % (port, ip)).run(ignore_errors=True)
            num = num + 1
            time.sleep(1)
            os.system("echo %s %s >> getip" % (port, ip))
        server = os.getenv('server')
        os.system("echo %s %s >> getip" % (local_ip, server))
        os.system("echo port_nums: %s >> getip" % len(self.test_port))
        return ip
